#
# \file
# \brief
# \author Marco Antonio Pinto Orellana
#
# -------------------------------------------------------------------------
#
# Copyright (C) 2012  (see AUTHORS file for a list of contributors)
# http://opentonitruum.sourceforge.net
#
# This file is part of OpenTonitruum - a simulator/library of signal trans-
# mission, modulation and processing in a wireless environment.
#
# OpenTonitruum is free software: you can redistribute it and/or modify it 
# under the terms of the Lesser GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at 
# your option) any later version.
#
# OpenTonitruum is distributed in the hope that it will be useful, but WITH-
# OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with OpenTonitruum.  If not, see <http://www.gnu.org/licenses/>.
#
# -------------------------------------------------------------------------
#

from SimPy.Simulation import *
from PhysicalEquipment import *
from DataStructure import *

HDLCFrameFormatType = Enum('Standard', 'Extended')
HDLCFrameType = Enum('Information', 'Supervision', 'Unnumbered')
HDLCRQ = Enum('Inactive', 'SelectiveRepetition', 'BackN')
HDLCTypeEquipment = Enum('Primary', 'Secondary')
HDLCProtocolState = Enum('Idle', 'Send', 'Receive')
HDLCStates = Enum('Idle', 'StartConnection', 'StartDataTransmission', 'EndConnection', 'StartDataReception')


file_tx = open('tx.txt', 'wt')
file_rx = open('rx.txt', 'wt')


def str2bvec(str):
	v = []
	for s in str: v.append(1 if int(s) > 0 else 0)
	return v

	
class BitField(object):
	def __init__(self, start = 0, length = 8, value = None, name = "", autoresizable = False):
		self.autoresizable = autoresizable
		self._groups = {}
		if value is not None and len(value) != length:
			raise Exception("Field length must be equals than definition.")
		self.value = (str2bvec(value) if value is not None else [0] * length)
		self.name = name
		self.start = start #Do not modify after define
		self._length = (length if length > 0 else 0) #Do not modify after define
		self._counter = 0
	
	def getLength(self):
		return self._length

	def setLength(self, value):
		self.value = self.value + [0] * (value - self._length)
		self._length = value
	
	length = property(getLength, setLength)
	
	def add(self, target = None, field = None, **args):
		if target is None:
			target = self
		else:
			target = self.__getSubItem(target)
		
		if field is None:
			field = BitField(**args)
		if field.start + field.length > target.length:
			if not self.autoresizable:
				raise Exception('Internal bit group exceed bit group size')
			else:
				target.length = field.start + field.length
		defaultvalue = field.value[:]
		field.value = target
		target._groups[field.name] = field
		target[field.name] = defaultvalue
		
	def append(self, target = None, field = None, **args):
		if target is None:
			target = self
		else:
			target = self.__getSubItem(target)
		
		if field is None:
			field = BitField(**args)
			
		field.start += target._counter
		target._counter = field.start + field.length
		if field.start + field.length > target.length:
			if not self.autoresizable:
				raise Exception('Internal bit group exceed bit group size')
			else:
				target.length = field.start + field.length
		defaultvalue = field.value[:]
		field.value = target
		target._groups[field.name] = field
		target[field.name] = defaultvalue
	
	def remove(self, name):
		del self._groups[name]
	
	def __getSubItem(self, index):
		if '.' in index:
			indices = index.strip().split('.')
			v = self._groups
			for ind in indices:
				v = v[ind]
			return v
		else:
			return self._groups[index]
	
	def __getitem__(self, index):
		if isinstance(index, str):# and index in self._groups:
			return self.__getSubItem(index)
		else:
			return self.value[self.start: self.start + self.length].__getitem__(index)
	
	def __setitem__(self, index, value):
		if isinstance(index, str):# and index in self._groups:
			destin = self.__getSubItem(index)
			partial_value = str2bvec(value)
			if len(partial_value) == destin.length:
				destin[:] = partial_value
			else:
				raise Exception("Assigned items modify original size.")
		else:
			partial_value = self.value[self.start: self.start + self.length]
			partial_value.__setitem__(index, value)
			if len(partial_value) == self.length:
				self.value[self.start: self.start + self.length] = partial_value
			else:
				raise Exception("Assigned items modify original size.")
	
	def __len__(self):
		return len(self.value[self.start : self.start + self.length])
	
	def __repr__(self):
		return repr(self.value[self.start : self.start + self.length])
	
	def to_int(self):
		value = self.value[self.start : self.start + self.length][:]
		value.reverse()
		return sum(v * 2 ** i for i, v in enumerate(value))

		
class HDLCFrame(BitField):

	flag = [0,1,1,1,1,1,1,0]
	
	def __init__(self, formatType, frameType, infoFieldLength = 8):
		BitField.__init__(self, autoresizable = True)
		self.formatType = formatType
		self._frameType = frameType
		self.infoFieldLength = infoFieldLength
		self._createFrame(formatType, frameType, infoFieldLength)
	
	def _createFrame(self, formatType, frameType, infoFieldLength):
		extended = (formatType != HDLCFrameFormatType.Standard)
		flagstart = BitField(name = 'flagstart', length = 8, value = "01111110")
		info = BitField(name = 'information', length = infoFieldLength)
		flagend = BitField(name = 'flagend', length = 8, value = "01111110")
		direction = self._createDirectionField(extended)
		fcs = self._createFcsField(extended)
		if frameType == HDLCFrameType.Information:
			control = self._createControlInformationField(extended)
		elif frameType == HDLCFrameType.Supervision:
			control = self._createControlSupervisionField(extended)
		elif frameType == HDLCFrameType.Unnumbered:
			control = self._createControlUnnumberedField(extended)
		else:
			raise Exception('Frame type not supported')
		self.append(field = flagstart)
		self.append(field = direction)
		self.append(field = control)
		self.append(field = info)
		self.append(field = fcs)
		self.append(field = flagend)
	
	def _createDirectionField(self, extended = False):
		if not extended:
			return BitField(name = 'direction', length = 8)
		else:
			return BitField(name = 'direction', length = 16)
	
	def _createFcsField(self, extended = False):
		if not extended:
			return BitField(name = 'fcs', length = 16)
		else:
			return BitField(name = 'fcs', length = 32)
	
	def _createControlInformationField(self, extended = False):
		if not extended:
			control = BitField(name = 'control', length = 8)
			control.append(name = 'flag', length = 1, value = "0")
			control.append(name = 'NS', length = 3)
			control.append(name = 'PF', length = 1)
			control.append(name = 'NR', length = 3)
		else:
			control = BitField(name = 'control', length = 16)
			control.append(name = 'flag', length = 1, value = "0")
			control.append(name = 'NS', length = 7)
			control.append(name = 'PF', length = 1)
			control.append(name = 'NR', length = 7)
		return control
	
	def _createControlSupervisionField(self, extended = False):
		if not extended:
			control = BitField(name = 'control', length = 8)
			control.append(name = 'flag', length = 2, value = "10")
			control.append(name = 'S', length = 2)
			control.append(name = 'PF', length = 1)
			control.append(name = 'NR', length = 3)
		else:
			control = BitField(name = 'control', length = 16)
			control.append(name = 'flag', length = 2, value = "10")
			control.append(name = 'S', length = 2)
			control.append(name = 'padding', length = 4)
			control.append(name = 'PF', length = 1)
			control.append(name = 'NR', length = 7)
		return control
	
	def _createControlUnnumberedField(self, extended = False):
		if not extended:
			control = BitField(name = 'control', length = 8)
			control.append(name = 'flag', length = 2, value = "11")
			control.append(name = 'M1', length = 2)
			control.append(name = 'PF', length = 1)
			control.append(name = 'M2', length = 3)
		else:
			control = BitField(name = 'control', length = 16)
			control.append(name = 'flag', length = 2, value = "11")
			control.append(name = 'M1', length = 2)
			control.append(name = 'PF', length = 1)
			control.append(name = 'M2', length = 3)
			control.append(name = 'padding', length = 7)
		return control
	
	#Only getters
	def getFrameType(self):
		#print "--->", self['control'][:]
		if self['control'][0] == 0:
			type = HDLCFrameType.Information
		elif self['control'][1] == 0:
			type = HDLCFrameType.Supervision
		elif self['control'][1] == 1:
			type = HDLCFrameType.Unnumbered
			self._frametype = type
		return type
	
	frameType = property(getFrameType)

	def isSupervision(self):
		return self.getFrameType() == HDLCFrameType.Supervision
		
	def isUnnumbered(self):
		return self.getFrameType() == HDLCFrameType.Unnumbered
		
	def isInformation(self):
		return self.getFrameType() == HDLCFrameType.Information
	
	#Getters/Setters
	def setPF(self, flag = True):
		self['control.PF'][0] = int(flag)
	
	def getPF(self):
		return self['control.PF'][:]
	
	PF = property(getPF, setPF)
	
	def setNS(self, NS):
		if self._frameType != HDLCFrameType.Information:
			raise Exception("Frame type doesn't contain N(S) bits")
		if not hasattr(NS, "__len__"):
			if NS >= (1<<len(self['control.NS'])) or NS < 0:
				raise Exception("NS length does not supported")
			NS = bin(NS)[2:]
			self['control.NS'] = "0" * (len(self['control.NS']) - len(NS)) + NS
		else:
			self['control.NS'] = NS
		
	def getNS(self):
		if self._frameType != HDLCFrameType.Information:
			raise Exception("Frame type doesn't contain N(S) bits")
		return self['control.NS']
	
	NS = property(getNS, setNS)
	
	def setNR(self, NR):
		if self._frameType != HDLCFrameType.Information and self._frameType != HDLCFrameType.Supervision:
			raise Exception("Frame type doesn't contain N(R) bits")
		if not hasattr(NR, "__len__"):
			if NR >= (1<<len(self['control.NR'])) or NR < 0:
				raise Exception("NR length does not supported")
			NR = bin(NR)[2:]
			self['control.NR'] = "0" * (len(self['control.NR']) - len(NR)) + NR
		else:
			self['control.NR'] = NR
		
	def getNR(self):
		if self._frameType != HDLCFrameType.Information and self._frameType != HDLCFrameType.Supervision:
			raise Exception("Frame type doesn't contain N(R) bits")
		return self['control.NR']
	
	NR = property(getNR, setNR)
	
	def setS(self, S):
		if self._frameType != HDLCFrameType.Supervision:
			raise Exception("Frame type doesn't contain S bit")
		if not hasattr(S, "__len__"):
			S = bin(S)[2:]
		self['control.S'] = S
		
	def getS(self):
		if self._frameType != HDLCFrameType.Supervision:
			raise Exception("Frame type doesn't contain S bit")
		return self['control.S']
	
	S = property(getS, setS)
	
	def setInformation(self, information):
		leninfo = len(self['information'])
		if self.getFrameType() != HDLCFrameType.Information:
			raise Exception("Frame type doesn't contain Information bit")
		if not hasattr(information, "__len__"):
			information = bin(information)[2:]
			information = '0'*(leninfo - len(information)) + information
		self['information'] = information
		
	def getInformation(self):
		if self.getFrameType() != HDLCFrameType.Information:
			raise Exception("Frame type doesn't contain Information bit")
		return self['information']
	
	information = property(getInformation, setInformation)
	
	def setM(self, M):
		if self.getFrameType() != HDLCFrameType.Unnumbered:
			raise Exception("Frame type doesn't contain M bit")
		if not hasattr(M, "__len__"):
			M = bin(M)[2:]
		if len(M) > 5 or len(M) < 0:
			raise Exception('M has an unsupported value')
		M1 = M[0:2]
		M2 = M[2:5]
		self['control.M1'] = M1
		self['control.M2'] = M2
	
	def getM(self):
		if self.getFrameType() != HDLCFrameType.Unnumbered:
			raise Exception("Frame type doesn't contain M bit")
		return self['control.M1'][:] + self['control.M2'][:]
	
	M = property(getM, setM)
	
	def setDirection(self, direction):
		lendir = len(self['direction'])
		if not hasattr(direction, "__len__"):
			direction = bin(direction)[2:]
			direction = '0'*(lendir - len(direction)) + direction
		self['direction'] = direction
		
	def getDirection(self):
		return self['direction']
	
	direction = property(getDirection, setDirection)
	
	def getDirectionAsInt(self):
		return self['direction'].to_int()
	
	direction_as_int = property(getDirectionAsInt)
	
	def __call__(self):
		obj = HDLCFrame(self.formatType, self._frameType, self.infoFieldLength)
		obj[:] = self[:]
		return obj
	
	def is_(self, other):
		return self['control.bits'] == other['control.bits'] 
	
	
	@staticmethod
	def createFrom(bits, formatType):
		infolength = len(bits) - 16
		if formatType == HDLCFrameFormatType.Standard:
			infolength = infolength - 8 - 8 - 16
			##infolength = len(bits) - len(HDLCFrame(formatType, HDLCFrameType.Information,0))
		elif formatType == HDLCFrameFormatType.Extended:
			infolength = infolength - 16 - 16 - 32
			##infolength = len(bits) - len(HDLCFrame(formatType, HDLCFrameType.Information,0))
		if infolength < 0:
			raise Exception('Bad bit HDLC sequence.')
		frame = HDLCFrame(formatType, HDLCFrameType.Information, infolength)
		#print len(frame[:]), len(bits), infolength
		frame[:] = bits
		frame.getFrameType()
		frame = HDLCFrame(formatType, frame.getFrameType(), infolength)
		#print len(frame[:]), len(bits)
		frame[:] = bits
		return frame
		
	@staticmethod
	def getCode(name):
		if name == 'SNRM': ret = "00001"
		elif name == 'SARM': ret = "11100"
		elif name == 'SABM': ret = "11100"
		elif name == 'DISC': ret = "00010"
		elif name == 'SNRME': ret = "11011"
		elif name == 'SARME': ret = "11010"
		elif name == 'SABME': ret = "11110"
		elif name == 'SIM': ret = "10010"
		elif name == 'UP': ret = "00100"
		elif name == 'UI': ret = "00000"
		elif name == 'XID': ret = "11101"
		elif name == 'RESET': ret = "11001"
		elif name == 'TEST': ret = "00111"
		elif name == 'DM': ret = "11000"
		elif name == 'UA': ret = "00110"
		elif name == 'FRMR': ret = "10001"
		elif name == 'RD': ret = "00010"
		elif name == 'RIM': ret = "10000"
		elif name == 'RR': ret = "00"
		elif name == 'RNR': ret = "00"
		elif name == 'REJ': ret = "00"
		elif name == 'SREJ': ret = "00"
		else: raise Exception('Unknown HDLC frame code.')
		return str2bvec(ret)
 

class HDLCTransreceiver(Transreceiver):
	def __init__(self, id, medium, format = HDLCFrameFormatType.Standard):
		Transreceiver.__init__(self, id, medium, [0,1,1,1,1,1,1,0])
		self.flagBuffer = Buffer(8)
		#self.flag = [0,1,1,1,1,1,1,0]
		self.frameBuffer = Buffer(1024*8)
		self.format = format
		self.correctFrame = Event()
		self.correctTransmition = Event()
		self.evaluateFrame = None
		self.__minbitlength = (48 if format == HDLCFrameFormatType.Standard else 80)
	
	def sendSupervisionFrame(self, S, NR, direction):
		type = HDLCFrameType.Supervision
		frame = HDLCFrame(self.format, type, 0)
		frame.S = (HDLCFrame.getCode(S) if isinstance(S, str) else S)
		frame.PF = 1
		frame.NR = NR
		frame.direction = direction
		self.send(frame)
		
	def sendUnnumberedFrame(self, M, direction):
		type = HDLCFrameType.Unnumbered
		frame = HDLCFrame(self.format, type, 0)
		frame.M = (HDLCFrame.getCode(M) if isinstance(M, str) else S)
		frame.PF = 1
		frame.direction = direction
		self.send(frame)
		
	def sendInformationFrame(self, NS, NR, information, direction):
		type = HDLCFrameType.Information
		frame = HDLCFrame(self.format, type, len(information))
		frame.PF = 1
		frame.NR = NR
		frame.NS = NS
		frame.direction = direction
		frame.information = information
		self.send(frame)
	
	def send(self, frame):
		Transreceiver.send(self, frame[:])
	
	def receive(self, target, bit):
		file_rx.write(str(bit))
		self.flagBuffer.push(bit)
		if self.flagBuffer[:] == self.flag:
			if not self.frameBuffer.empty():
				self.frameBuffer.push(bit)
				frame = HDLCFrame.createFrom(self.removeZeroBits(self.frameBuffer[:]), self.format)
				if self.evaluateFrame is None or self.evaluateFrame(self, frame):
					self.correctFrame(self, frame)
					file_rx.write("\n")
				self.frameBuffer.clear()
			else:
				for b in self.flag: self.frameBuffer.push(b)
		elif not self.frameBuffer.empty():
			self.frameBuffer.push(bit)
	
	def transmitEnd(self, target, bit):
		file_tx.write(str(bit))
		self.correctTransmition(target, bit)

		
class HDLCProtocol(Process):
	def __init__(self, id, medium, rq = HDLCRQ.Inactive, type = HDLCTypeEquipment.Secondary):
		Process.__init__(self)
		self.id = id
		self.destin_id = -1
		self.medium = medium
		self.rq = rq
		self.type = type
		self.transreceiver = HDLCTransreceiver(id, medium)
		self.transreceiver.evaluateFrame = self.evaluateFrame
		self.transreceiver.correctFrame.add(self.receivedFrame)
		self.transreceiver.correctTransmition.add(self.correctTransmition)
		
		self.receivedData = Event()
		
		self.state = HDLCStates.Idle
		self.flags_received = False
		self.flags_transmited = False
		self.currentFrame = None
		self.VS = 0
		self.VR = 0
		self.bufferTx = Buffer()
		self.bufferDataTx = Buffer()
		activate(self, self.procedure())
	
	def correctTransmition(self, target, frame):
		self.flags_transmited = True
	
	def evaluateFrame(self, target, frame):
		return True #(frame['direction'].to_int() == self.id or frame['direction'].to_int() == self.destin_id)
	
	def receivedFrame(self, target, frame):
		self.flags_received = True
		self.currentFrame = frame
	
	def expect(self, condition, *p):
		timer_interval, timer_maximum = 0.01, 1000
		ctime = now()
		while now() < ctime + timer_maximum:
			if self.flags_received:
				self.flags_received = False
				if condition(self.currentFrame):
					return
			if now() >= ctime + timer_maximum - timer_interval: raise Exception("Frame not received.")
			yield hold, self, timer_interval
		raise Exception("Frame not received.")
		
	def expectEternalFrame(self, condition):
		timer_interval, timer_maximum = 0.1, 10
		yield waituntil, self, lambda: self.flags_received
		self.flags_received = False
		if condition(self.currentFrame):
			return
	
	def send(self, direction, data):
		bufferData = Buffer()
		for d in data: bufferData.push(d)
		self.bufferDataTx.push((bufferData, direction))
	
	def procedure(self):
		expectUA = lambda frame: frame.isUnnumbered() and frame.M == HDLCFrame.getCode("UA") and frame.direction_as_int == self.destin_id
		expectSABM = lambda frame: frame.isUnnumbered() and frame.M == HDLCFrame.getCode("SABM") and frame.direction_as_int == self.id
		expectResp = lambda frame: frame.isSupervision()# and frame.direction_as_int == self.destin_id
		interval = 0.1
		while True:
			if self.state == HDLCStates.Idle:
				yield waituntil, self, lambda: self.flags_received or len(self.bufferDataTx) != 0
				if self.flags_received:
					if expectSABM(self.currentFrame):
						self.transreceiver.sendUnnumberedFrame("UA", self.id)
						self.state = HDLCStates.StartDataReception
					self.flags_received = False
				else:
					self.state = HDLCStates.StartConnection
					buffer, direction = self.bufferDataTx.pop()
					for d in buffer: self.bufferTx.push(d)
					self.destin_id = direction
			elif self.state == HDLCStates.StartConnection:
				self.transreceiver.sendUnnumberedFrame("SABM", self.destin_id)
				for _subprocess in self.expect(expectUA): yield _subprocess
				self.state = HDLCStates.StartDataTransmission
			elif self.state == HDLCStates.StartDataTransmission:
				if not self.bufferTx.empty():
					data = self.bufferTx.pop()
					self.transreceiver.sendInformationFrame(self.VS, self.VR, data, self.destin_id)
					yield hold, self, 0.1
					for _subprocess in self.expect(expectResp): yield _subprocess
				else:
					self.state = HDLCStates.EndConnection
			elif self.state == HDLCStates.EndConnection:
				self.transreceiver.sendUnnumberedFrame("DISC", self.destin_id)
				for _subprocess in self.expect(expectUA): yield _subprocess
				self.state = HDLCStates.Idle
			elif self.state == HDLCStates.StartDataReception:
				yield waituntil, self, lambda: self.flags_received
				if self.currentFrame.isInformation() and self.currentFrame.direction_as_int == self.id:
					print "id:", self.id, "Received data info: ", self.currentFrame.information[:]
					self.receivedData(self.currentFrame, self.currentFrame.information[:])
					self.transreceiver.sendSupervisionFrame("RR", self.VR, self.id)
				self.flags_received = False
				if self.currentFrame.isUnnumbered() and self.currentFrame.M == HDLCFrame.getCode("DISC"):
					self.transreceiver.sendUnnumberedFrame("UA", self.id)
					self.state = HDLCStates.Idle
	